package com.joker.airoleplayplatformback.listener;

import cn.hutool.core.io.FileUtil;
import com.joker.airoleplayplatformback.domain.event.VoiceProcessingEvent;
import com.joker.airoleplayplatformback.domain.po.AiAgentPO;
import com.joker.airoleplayplatformback.domain.po.ChatMessagePO;
import com.joker.airoleplayplatformback.service.ChatService;
import com.joker.airoleplayplatformback.service.KnowledgeBaseService;
import com.joker.airoleplayplatformback.service.OssService;
import com.joker.airoleplayplatformback.service.PerformanceMonitorService;
import com.joker.airoleplayplatformback.utils.AudioFormatConverter;
import com.joker.airoleplayplatformback.utils.SpeechRecognitionUtils;
import com.joker.airoleplayplatformback.utils.TextToSpeechUtils;
import com.joker.airoleplayplatformback.utils.WebSocketConnectionManager;
import com.vdurmont.emoji.EmojiParser;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * 语音处理监听器 - 异步处理语音对话流程
 *
 * @author feixiang.li
 * @since 2025-09-27
 */
@Slf4j
@Component
public class VoiceProcessingListener implements ApplicationListener<VoiceProcessingEvent> {


    @Autowired
    private ChatService chatService;

    @Autowired
    private KnowledgeBaseService knowledgeBaseService;

    @Autowired
    private OssService ossService;

    @Autowired
    @Qualifier("taskExecutor")
    Executor executor;

    @Autowired
    private ChatModel chatModel;

    @Autowired
    private PerformanceMonitorService performanceMonitorService;

    @Autowired
    private WebSocketConnectionManager connectionManager;

    @Value("${langchain4j.open-ai.chat-model.api-key}")
    private String apiKey;

    // 存储WebSocket会话对应的聊天信息
    private final Map<String, ChatSessionInfoDTO> sessionInfoMap = new ConcurrentHashMap<>();

    @Override
    @Async("voiceProcessingExecutor")
    public void onApplicationEvent(VoiceProcessingEvent event) {
        StopWatch stopWatch = new StopWatch("VoiceProcessing-" + event.getWebsocketSessionId());

        try {
            log.info("开始异步处理语音对话: websocketSessionId={}, audioFile={}", event.getWebsocketSessionId(), event.getAudioFilePath());

            processVoiceAsync(event.getWebsocketSessionId(), event.getAudioFilePath(), stopWatch);

        } catch (Exception e) {
            log.error("异步处理语音对话失败: websocketSessionId={}", event.getWebsocketSessionId(), e);
        } finally {
            if (stopWatch.isRunning()) {
                stopWatch.stop();
            }

            // 记录性能数据
            performanceMonitorService.recordCompleteProcess(stopWatch);

            // 检查性能瓶颈
            performanceMonitorService.checkPerformanceBottleneck();

            log.info("语音处理完成，总耗时: {}ms", stopWatch.getTotalTimeMillis());
        }
    }

    /**
     * 异步处理语音对话
     */
    private void processVoiceAsync(String websocketSessionId, String audioFilePath, StopWatch stopWatch) {
        ChatSessionInfoDTO sessionInfo = sessionInfoMap.get(websocketSessionId);
        if (sessionInfo == null) {
            log.error("无法处理语音对话，会话信息不存在: websocketSessionId:{}", websocketSessionId);
            return;
        }
        try {
            // 1. 验证和预处理音频文件
            stopWatch.start("验证音频文件");
            File audioFile = new File(audioFilePath);

            // 检查文件是否存在和有效
            if (!audioFile.exists() || audioFile.length() == 0) {
                log.error("音频文件不存在或为空: {}", audioFilePath);
                return;
            }

            // 获取音频文件信息
            AudioFormatConverter.AudioFileInfo audioInfo = AudioFormatConverter.getAudioFileInfo(audioFilePath);
            if (audioInfo != null) {
                log.info("音频文件信息: {}", audioInfo);
            }

            stopWatch.stop();
            log.info("音频文件验证完成, 耗时: {}ms", stopWatch.getLastTaskTimeMillis());

            // 2. 上传音频文件到OSS
            stopWatch.start("上传音频到OSS");
            String audioUrl = ossService.uploadFile(audioFile, "voice/" + audioFile.getName());
            stopWatch.stop();
            log.info("音频上传成功: {}, 耗时: {}ms", audioUrl, stopWatch.getLastTaskTimeMillis());

            // 3. 调用ASR服务进行语音识别
            stopWatch.start("ASR语音识别");
            String recognizedText = SpeechRecognitionUtils.recognizeSpeechByUrl(audioUrl, apiKey);
            stopWatch.stop();
            if (recognizedText == null || recognizedText.trim().isEmpty()) {
                log.warn("语音识别结果为空，可能的原因：1.音频文件格式不支持 2.音频内容为空 3.ASR服务异常");
                // 发送错误消息给客户端
                if (connectionManager.isSessionActive(websocketSessionId)) {
                    connectionManager.sendErrorMessage(websocketSessionId, "语音识别失败，请重试");
                }
                return;
            }
            log.info("语音识别结果: {}, 耗时: {}ms", recognizedText, stopWatch.getLastTaskTimeMillis());

            // 4. 保存用户消息
            stopWatch.start("保存用户消息");
            String userMessageId = UUID.randomUUID().toString();
            ChatMessagePO userMessage = new ChatMessagePO();
            userMessage.setMessageId(userMessageId);
            userMessage.setSessionId(sessionInfo.getChatSessionId());
            userMessage.setMessageType("user");
            userMessage.setContent(recognizedText);
            userMessage.setAudioUrl(audioUrl);
            userMessage.setStatus(0);
            chatService.saveMessage(userMessage);
            stopWatch.stop();
            log.info("用户消息保存完成, 耗时: {}ms", stopWatch.getLastTaskTimeMillis());

            // 5. 获取对话历史并构建上下文
            stopWatch.start("构建对话上下文");
            List<ChatMessagePO> recentMessages = chatService.getRecentMessages(sessionInfo.getChatSessionId(), 10);
            List<dev.langchain4j.data.message.ChatMessage> chatMessages = buildChatContext(sessionInfo.getAgent(), recentMessages, recognizedText, sessionInfo.getUserId());
            stopWatch.stop();
            log.info("对话上下文构建完成, 历史消息数: {}, 耗时: {}ms", recentMessages.size(), stopWatch.getLastTaskTimeMillis());

            // 6. 调用大模型生成回复
            stopWatch.start("LLM生成回复");
            ChatResponse chat = chatModel.chat(chatMessages);
            String aiReplyText = chat.aiMessage().text();
            // 如果不为空，则去除表情包
            if (StringUtils.isNotBlank(aiReplyText)) {
                aiReplyText = EmojiParser.removeAllEmojis(aiReplyText);
            }
            stopWatch.stop();
            log.info("AI回复生成完成: {}, 耗时: {}ms", aiReplyText, stopWatch.getLastTaskTimeMillis());

            // 7. 调用TTS生成语音
            stopWatch.start("TTS语音合成");
            TextToSpeechUtils.TTSResponse ttsResponse = TextToSpeechUtils.textToSpeech(aiReplyText, sessionInfo.getAgent().getDefaultVoiceType(), "mp3", apiKey);
            stopWatch.stop();
            log.info("TTS语音合成完成, 音频时长: {}ms, 合成耗时: {}ms", ttsResponse.getDuration(), stopWatch.getLastTaskTimeMillis());
            final String aiReplyTextFinal = aiReplyText;
            executor.execute(() -> {
                try {
                    // 8. 上传AI回复的音频
                    String aiAudioFileName = "ai_reply_" + UUID.randomUUID() + ".mp3";
                    String aiAudioTempPath = System.getProperty("java.io.tmpdir") + File.separator + aiAudioFileName;
                    ttsResponse.saveToFile(aiAudioTempPath);
                    File aiAudioFile = new File(aiAudioTempPath);
                    String aiAudioUrl = ossService.uploadFile(aiAudioFile, "voice/" + aiAudioFile.getName());
                    log.info("AI回复音频上传成功: {}", aiAudioUrl);
                    // 9. 保存AI回复消息
                    String aiMessageId = UUID.randomUUID().toString();
                    ChatMessagePO aiMessage = new ChatMessagePO();
                    aiMessage.setMessageId(aiMessageId);
                    aiMessage.setSessionId(sessionInfo.getChatSessionId());
                    aiMessage.setMessageType("assistant");
                    aiMessage.setContent(aiReplyTextFinal);
                    aiMessage.setAudioUrl(aiAudioUrl);
                    aiMessage.setVoiceType(sessionInfo.getAgent().getDefaultVoiceType());
                    aiMessage.setAudioDuration(ttsResponse.getDuration());
                    aiMessage.setStatus(0);
                    chatService.saveMessage(aiMessage);
                } catch (Exception e) {
                    log.error("保存AI回复消息失败", e);
                } finally {
                    FileUtil.del(audioFile);
                }
            });


            // 10. 通过WebSocket发送回复给客户端
            stopWatch.start("发送WebSocket消息");
            if (connectionManager.isSessionActive(websocketSessionId)) {
                // 发送MP3音频数据
                connectionManager.sendMp3AudioMessage(websocketSessionId, ttsResponse.getAudioData());
            }
            stopWatch.stop();
            log.info("WebSocket消息发送完成, 耗时: {}ms", stopWatch.getLastTaskTimeMillis());

        } catch (Exception e) {
            log.error("异步处理语音对话失败", e);
            // 发送错误消息给客户端
            if (connectionManager.isSessionActive(websocketSessionId)) {
                connectionManager.sendErrorMessage(websocketSessionId, "语音处理失败: " + e.getMessage());
            }
        }
    }

    /**
     * 构建聊天上下文
     */
    private List<dev.langchain4j.data.message.ChatMessage> buildChatContext(AiAgentPO agent, List<ChatMessagePO> recentMessages, String currentUserMessage, Long userId) {
        List<dev.langchain4j.data.message.ChatMessage> messages = new ArrayList<>();

        // 构建系统提示词
        String systemPrompt = agent.getSystemPrompt();

        // 如果智能体启用状态为1，则调用知识库增强上下文
        if (agent.getStatus() != null && agent.getStatus() == 1) {
            try {
                log.info("智能体已启用，开始搜索知识库增强上下文: agentId={}, userId={}", agent.getId(), userId);

                // 搜索相关知识库内容
                List<String> knowledgeResults = knowledgeBaseService.search(userId, currentUserMessage);

                if (knowledgeResults != null && !knowledgeResults.isEmpty()) {
                    log.info("找到相关知识库内容，条数: {}", knowledgeResults.size());

                    // 将知识库内容添加到系统提示词中
                    StringBuilder enhancedPrompt = new StringBuilder(systemPrompt);
                    enhancedPrompt.append("\n\n相关知识库内容以及用户信息：\n");

                    // 限制最多3条结果
                    for (int i = 0; i < Math.min(knowledgeResults.size(), 3); i++) {
                        enhancedPrompt.append("- ").append(knowledgeResults.get(i)).append("\n");
                    }

                    enhancedPrompt.append("\n 请基于以上知识库内容和对话历史来回答用户的问题。");
                    systemPrompt = enhancedPrompt.toString();

                    log.info("知识库内容已添加到系统提示词中");
                } else {
                    log.info("未找到相关知识库内容");
                }
            } catch (Exception e) {
                log.error("搜索知识库失败，继续使用原始系统提示词", e);
            }
        } else {
            log.info("智能体未启用或状态异常，跳过知识库搜索: agentId={}, status={}", agent.getId(), agent.getStatus());
        }

        // 添加系统提示词
        messages.add(SystemMessage.from(systemPrompt));

        // 添加历史对话
        for (ChatMessagePO message : recentMessages) {
            if ("user".equals(message.getMessageType())) {
                messages.add(UserMessage.from(message.getContent()));
            } else if ("assistant".equals(message.getMessageType())) {
                messages.add(AiMessage.from(message.getContent()));
            }
        }

        return messages;
    }


    /**
     * 注册会话信息
     */
    public void registerSessionInfo(String websocketSessionId, ChatSessionInfoDTO sessionInfo) {
        log.info("注册会话信息:{}: websocketSessionId:{}", sessionInfo, websocketSessionId);
        sessionInfoMap.put(websocketSessionId, sessionInfo);
    }

    /**
     * 移除会话信息
     */
    public void unregisterSessionInfo(String websocketSessionId) {
        sessionInfoMap.remove(websocketSessionId);
    }

    /**
     * 聊天会话信息DTO
     */
    @Data
    public static class ChatSessionInfoDTO {
        private Long userId;
        private AiAgentPO agent;
        private String chatSessionId;
    }
}
